#pragma once

#include "Epoll.hpp"
#include "Sock.hpp"
#include <memory>

class Epollserver
{
    static const int default_array_num = 1024;

public:
    Epollserver(uint16_t port)
        : _epoll_ptr(new Epoll),
          _sock_ptr(new Sock(port))
    {
    }

    void Init()
    {
        // 1. 创建套接字
        // 2. 进行绑定
        // 3. 设置监听
        // 4. 将网络套接字 ,加入到epoll模型中

        _sock_ptr->Socket();
        _sock_ptr->Bind();
        _sock_ptr->Listen();

        _epoll_ptr->Add_fd(_sock_ptr->Get_fd(), EPOLLIN);
    }

    void Sockfd_Ready()
    {
        // 网络套接字就绪

        // 1. 获取建立文件描述符
        // 2. 将文件描述符加入到epoll模型中

        int newfd = _sock_ptr->Accept();
        _epoll_ptr->Add_fd(newfd, EPOLLIN);
    }

    void Normalfd_Ready(int fd)
    {
        // 普通文件描述符就绪

        // 1. 将数据读取上来
        // 2. 向用户端返回信息

        char buffer[1024];
        int n = read(fd, buffer, sizeof(buffer) - 1);
        if (n > 0)
        {
            buffer[n] = 0;
            std::string ret = "server got a message : ";
            ret += buffer;

            write(fd, ret.c_str(), ret.size());
        }
        else if (n == 0)
        {
            // 对方将文件关闭
            // 1. 此处我们也就不需要在进行等待了, 从epoll模型中移除
            // 2. 关闭文件描述符
            _epoll_ptr->Del_fd(fd);
            close(fd);
        }
        else
        {
            // 出错了
            // 1. 打印日志
            // 2. 将文件描述符从epoll模型中移除
            // 3. 关闭文件描述符
            Log(Warning) << "read fail";
            _epoll_ptr->Del_fd(fd);
            close(fd);
        }
    }

    void Dispatcher(int n)
    {
        int listensock = _sock_ptr->Get_fd();
        for (int i = 0; i < n; i++)
        {
            int fd = _ep_array[i].data.fd;
            if (fd == listensock)
            {
                Sockfd_Ready();
            }
            else
            {
                Normalfd_Ready(fd);
            }
        }
    }

    void Run()
    {
        while (1)
        {
            int n = _epoll_ptr->Wait(_ep_array, default_array_num, -1);
            if (n > 0)
            {
                Dispatcher(n);
            }
            else if( n == 0)
            {
                Log(Info) << "no file is ready";
            }
            else
            {
                Log(Warning) << "epoll wait fail";
            }
            
        }
    }

private:
    std::shared_ptr<Epoll> _epoll_ptr;
    std::shared_ptr<Sock> _sock_ptr;
    struct epoll_event _ep_array[default_array_num];
};